home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 June
/
EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso
/
earcd
/
utmisc1
/
chktex.lha
/
chktex
/
FindErrs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-04-30
|
37KB
|
1,449 lines
/*
* ChkTeX v1.4, error searching & report routines.
* Copyright (C) 1995-96 Jens T. Berger Thielemann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Contact the author at:
* Jens Berger
* Spektrumvn. 4
* N-0666 Oslo
* Norway
* E-mail: <jensthi@ifi.uio.no>
*
*
*/
#include "ChkTeX.h"
#define INTFAULTMSG \
"INTERNAL FAULT OCCURED! PLEASE SUBMIT A BUG REPORT!\n"
#ifndef __LOCALIZED
char InternFault [] = INTFAULTMSG;
# define INTERNFAULT &InternFault[0]
#else
# define INTERNFAULT INTFAULTMSG
#endif
STRPTR VerbClear = "|";
BOOL AtLetter = FALSE, /* Whether `@' is a letter or not. */
InHeader = TRUE, /* Whether we're in the header */
VerbMode = FALSE; /* Whether we're in complete ignore-mode */
LONG MathMode; /* Whether we're in math mode or not */
STRPTR VerbStr; /* String we'll terminate verbmode upon */
/***************************** ERROR MESSAGES ***************************/
struct ErrMsg LaTeXMsgs [emMaxFault + 1] =
{
{emMinFault, etErr, TRUE, ctNone,
INTERNFAULT},
{emSpaceTerm, etWarn, TRUE, ctOutMath,
"Command terminated with space."},
{emNBSpace, etWarn, TRUE, ctNone,
"Non-breaking space (`~') should have been used."},
{emEnclosePar, etWarn, TRUE, ctInMath,
"You should enclose the previous parenthesis with `{}\'."},
{emItInNoIt, etWarn, TRUE, ctOutHead,
"Italic correction (`\\/') found in non-italic buffer."},
{emItDup, etWarn, TRUE, ctNone,
"Italic correction (`\\/') found more than once."},
{emNoItFound, etWarn, TRUE, ctOutHead,
"No italic correction (`\\/') found."},
{emAccent, etWarn, TRUE, ctNone,
"Accent command `%s' needs use of `\\%c%s'."},
{emWrongDash, etWarn, TRUE, ctOutMath,
"Wrong length of dash may have been used."},
{emExpectC, etWarn, TRUE, ctNone,
"`%s' expected, found `%s'."},
{emSoloC, etWarn, TRUE, ctNone,
"Solo `%s' found."},
{emEllipsis, etWarn, TRUE, ctNone,
"You should use %s to achieve an ellipsis."},
{emInterWord, etWarn, TRUE, ctOutMath,
"Interword spacing (`\\ ') should perhaps be used."},
{emInterSent, etWarn, TRUE, ctOutMath,
"Intersentence spacing (`\\@') should perhaps be used."},
{emNoArgFound, etErr, TRUE, ctNone,
"Could not find argument for command."},
{emNoMatchC, etWarn, TRUE, ctOutMath,
"No match found for `%s'."},
{emMathStillOn, etWarn, TRUE, ctNone,
"Mathmode still on at end of LaTeX file."},
{emNoMatchCC, etWarn, TRUE, ctNone,
"Number of `%c' doesn't match the number of `%c'!"},
{emUseQuoteLiga, etWarn, TRUE, ctNone,
"Use either `` or '' as an alternative to `\"'."},
{emUseOtherQuote, etWarn, TRUE, ctNone,
"Use \"'\" (ASCII 39) instead of \"´\" (ASCII 180)."},
{emUserWarn, etWarn, TRUE, ctNone,
"User-specified pattern found."},
{emNotIntended, etWarn, FALSE, ctNone,
"This command might not be intended."},
{emComment, etMsg, FALSE, ctNone,
"Comment displayed."},
{emThreeQuotes, etWarn, TRUE, ctNone,
"Either %c\\,%c%c or %c%c\\,%c will look better."},
{emFalsePage, etWarn, TRUE, ctNone,
"Delete this space to maintain correct pagereferences."},
{emEmbrace, etWarn, TRUE, ctInMath,
"You might wish to put this between a pair of `{}'"},
{emSpacePunct, etWarn, TRUE, ctOutMath,
"You ought to remove spaces in front of punctuation."},
{emNoCmdExec, etWarn, TRUE, ctNone,
"Could not execute LaTeX command."},
{emItPunct, etWarn, TRUE, ctNone,
"Don't use \\/ in front of small punctuation."},
{emUseTimes, etWarn, TRUE, ctNone,
"$\\times$ may look prettier here."},
{emMultiSpace, etWarn, FALSE, ctOutMath,
"Multiple spaces detected in input."},
{emIgnoreText, etWarn, TRUE, ctNone,
"This text may be ignored."},
{emBeginQ, etWarn, TRUE, ctOutMath,
"Use ` to begin quotation, not '."},
{emEndQ, etWarn, TRUE, ctOutMath,
"Use ' to end quotation, not `."},
{emQuoteMix, etWarn, TRUE, ctNone,
"Don't mix quotes."},
{emWordCommand, etWarn, TRUE, ctInMath,
"You should perhaps use `\\%s' instead."},
{emMaxFault, etErr, TRUE, 0,
INTERNFAULT}
};
struct ErrMsg PrgMsgs [pmMaxFault + 1] =
{
{pmMinFault, etErr, TRUE, 0,
INTERNFAULT},
{pmUnknownTerm, etErr, TRUE, 0,
"Unknown terminal type - using normal verbosity."},
{pmNoFileMatch, etWarn, TRUE, 0,
"No files matched the pattern `%s'."},
{pmNoTeXOpen, etErr, TRUE, 0,
"Unable to open the TeX file `%s'."},
{pmRename, etMsg, TRUE, 0,
"Renaming `%s' as `%s'."},
{pmRenameErr, etErr, TRUE, 0,
"Could not rename `%s' to `%s'."},
{pmOutOpen, etErr, TRUE, 0,
"Unable to open output file."},
{pmOutTwice, etErr, TRUE, 0,
"You can specify output file only once."},
{pmStrDupErr, etErr, TRUE, 0,
"Unable to duplicate strings - no memory?"},
{pmWordListErr, etErr, TRUE, 0,
"Unable to create wordlist - no memory?"},
{pmNoStackMem, etErr, TRUE, 0,
"Unable to create stack - no memory?\n"},
{pmWarnNumErr, etErr, TRUE, 0,
"Illegal warning number used."},
{pmVerbLevErr, etErr, TRUE, 0,
"Illegal verbosity level."},
{pmNotPSDigit, etWarn, TRUE, 0,
"`%c' is not a %s digit - ignored!"},
{pmEscCode, etWarn, TRUE, 0,
"Unknown escape code `%c%c' - ignored!"},
{pmKeyWord, etErr, TRUE, 0,
"Unsupported control word (`%s') encountered in file `%s'."},
{pmFaultFmt, etErr, TRUE, 0,
"\"%s\", line %d: Faulty format - unexpected %s found."},
{pmRsrcOpen, etWarn, TRUE, 0,
"Could not open `%s', may cause unwanted behaviour."},
{pmSlowAbbr, etMsg, FALSE, 0,
"The abbreviation `%s' requires slow abbreviation searching."},
{pmEmptyToken, etErr, TRUE, 0,
"Empty token isolated in `%s' - probably faulty format"},
{pmAssert, etErr, TRUE, 0,
"Assertion failed. Please report bug."},
{pmNoRsrc, etWarn, TRUE, 0,
"Could not find global resource file."},
{pmMaxFault, etErr, TRUE, 0,
INTERNFAULT},
};
/*
* %b - string to print Between fields (from -s option)
* %c - Column position of error
* %d - lenght of error (Digit)
* %f - current Filename
* %i - Turn on inverse printing mode.
* %I - Turn off inverse printing mode.
* %k - Kind of error (warning, error, message)
* %l - Line number of error
* %m - warning Message
* %n - warning Number
* %u - an Underlining line (like the one which appears when using -v1)
* %r - part of line in front of error ('S' - 1)
* %s - part of line which contains error (String)
* %t - part of line after error ('S' + 1)
*/
#define istex(c) (isalpha(c) || (AtLetter && (c == '@')))
#define CTYPE(func) \
static int my_##func(int c) \
{ \
return(func(c)); \
}
#define INUSE(c) (LaTeXMsgs[(enum ErrNum) c].InUse)
#define PSERRA(pos,len,err,a) \
PrintError(CurStkName(&InputStack), RealBuf, pos, len, Line, err, a);
#define HEREA(len, err, a) PSERRA(BufPtr - Buf - 1, len, err, a)
#define PSERR(pos,len,err) PSERRA(pos,len,err,"")
#define HERE(len, err) HEREA(len, err, "")
#define SKIP_BACK(ptr, c, check) \
while((c = *ptr--)) \
{ \
ifn(check) \
break; \
} \
ptr++;
#define SKIP_AHEAD(ptr, c, check) \
while((c = *ptr++)) \
{ \
ifn(check) \
break; \
} \
ptr--;
/* -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- -=><=- */
static ULONG Line;
static STRPTR RealBuf, LineCpy, BufPtr;
NEWBUF(Buf, BUFSIZ);
NEWBUF(CmdBuffer, BUFSIZ);
NEWBUF(ArgBuffer, BUFSIZ);
CTYPE(isdigit)
CTYPE(isalpha)
static STRPTR MakeCpy(void)
{
if(!LineCpy)
LineCpy = strdup(RealBuf);
if(!LineCpy)
PrintPrgErr(pmStrDupErr);
return(LineCpy);
}
static STRPTR PreProcess(void)
{
/* First, kill comments. */
STRPTR TmpPtr;
strcpy(Buf, RealBuf);
TmpPtr = Buf;
while((TmpPtr = strchr(TmpPtr, '%')))
{
if(TmpPtr[-1] != '\\')
{
PSERR(TmpPtr - Buf, 1, emComment);
*TmpPtr = 0;
break;
}
TmpPtr++;
}
return(Buf);
}
/*
* Interpret environments
*/
static void PerformEnv(STRPTR Env, int Begin)
{
static
UBYTE VBStr [BUFSIZ] = "";
if(HasWord(Env, &MathEnvir))
{
MathMode += Begin ? 1 : -1;
MathMode = max(MathMode, 0);
}
if(HasWord(Env, &VerbEnvir))
{
VerbMode = Begin;
sprintf(VBStr, "\\end{%s}", Env);
VerbStr = VBStr;
}
}
static STRPTR SkipVerb(void)
{
STRPTR TmpPtr = BufPtr;
int TmpC;
if(VerbMode && BufPtr)
{
ifn(TmpPtr = strstr(BufPtr, VerbStr))
BufPtr = &BufPtr[strlen(BufPtr)];
else
{
VerbMode = FALSE;
BufPtr = &TmpPtr[strlen(VerbStr)];
SKIP_AHEAD(BufPtr, TmpC, LATEX_SPACE(TmpC));
if(*BufPtr)
PSERR(BufPtr - Buf, strlen(BufPtr) - 2, emIgnoreText);
}
}
return(TmpPtr);
}
#define CHECKDOTS(wordlist, dtval) \
for(i = 0; (i < wordlist.Stack.Used) && !(Back && Front); i++) \
{ if(!strafter(PstPtr, wordlist.Stack.Data[i])) \
Back = dtval; \
if(!strinfront(PrePtr, wordlist.Stack.Data[i])) \
Front = dtval; }
/*
* Checks that the dots are correct
*/
static enum DotLevel CheckDots(STRPTR PrePtr, STRPTR PstPtr)
{
ULONG i;
int TmpC;
enum DotLevel Front = dtUnknown, Back = dtUnknown;
if(MathMode)
{
PrePtr--;
SKIP_BACK(PrePtr, TmpC, (LATEX_SPACE(TmpC) || strchr("{}", TmpC)));
SKIP_AHEAD(PstPtr, TmpC, (LATEX_SPACE(TmpC)|| strchr("{}", TmpC)));
CHECKDOTS(CenterDots, dtCDots);
ifn(Front && Back)
{
CHECKDOTS(LowDots, dtLDots);
}
return(Front & Back);
}
else
return(dtLDots);
}
static STRPTR Dot2Str(enum DotLevel dl)
{
STRPTR Retval = INTERNFAULT;
switch(dl)
{
case dtUnknown:
Retval = "\\cdots or \\ldots";
break;
case dtDots:
Retval = "\\dots";
break;
case dtCDots:
Retval = "\\cdots";
break;
case dtLDots:
Retval = "\\ldots";
break;
}
return Retval;
}
/*
* Interpret isolated commands.
*
*/
void PerformBigCmd(STRPTR CmdPtr)
{
STRPTR TmpPtr,
ArgEndPtr = GetLTXArg(BufPtr, ArgBuffer, '{'); /*}*/
ULONG CmdLen = strlen(CmdBuffer);
UBYTE TmpC;
enum ErrNum
ErrNum;
struct ErrInfo *ei;
enum DotLevel dotlev, realdl = dtUnknown;
/* Kill `\verb' commands */
if(WipeVerb)
{
if(!strcmp(CmdBuffer, "\\verb"))
{
if(*BufPtr)
{
if((TmpPtr = strchr(&BufPtr[1], *BufPtr)))
strwrite(CmdPtr, VerbClear, (TmpPtr - CmdPtr) + 1);
else
PSERR(CmdPtr - Buf, 5, emNoArgFound);
}
}
}
if(HasWord(CmdBuffer, &IJAccent))
{
if(ArgEndPtr)
{
TmpPtr = ArgBuffer;
SKIP_AHEAD(TmpPtr, TmpC, TmpC == '{'); /* } */
if(strchr("ij", *TmpPtr))
{
if(TmpPtr[-1] != '\\')
PrintError(CurStkName(&InputStack), RealBuf,
CmdPtr - Buf,
(LONG) strlen(CmdBuffer), Line,
emAccent, CmdBuffer,
*TmpPtr, MathMode? "math" : "");
}
}
else
PSERR(CmdPtr - Buf, CmdLen, emNoArgFound);
}
if(!strcmp(CmdBuffer, "\\begin") ||
!strcmp(CmdBuffer, "\\end"))
{
if(ArgEndPtr)
{
if(!strcmp(ArgBuffer, "document"))
InHeader = FALSE;
if(CmdBuffer[1] == 'b')
{
ifn(PushErr(ArgBuffer, Line, CmdPtr - Buf,
CmdLen, MakeCpy(),
&EnvStack))
PrintPrgErr(pmNoStackMem);
}
else
{
if((ei = PopErr(&EnvStack)))
{
if(strcmp(ei->Data, ArgBuffer))
PrintError(CurStkName(&InputStack), RealBuf,
CmdPtr - Buf,
(LONG) strlen(CmdBuffer),
Line, emExpectC, ei->Data, ArgBuffer);
FreeErrInfo(ei);
}
else
PrintError(CurStkName(&InputStack), RealBuf,
CmdPtr - Buf,
(LONG) strlen(CmdBuffer),
Line, emSoloC, ArgBuffer);
}
PerformEnv(ArgBuffer, CmdBuffer[1] == 'b');
} else
PSERR(CmdPtr - Buf, CmdLen, emNoArgFound);
}
if((ErrNum = PerformCommand(CmdBuffer, BufPtr)))
PSERR(CmdPtr - Buf, CmdLen, ErrNum);
if(!strcmp(CmdBuffer, "\\cdots"))
realdl = dtCDots;
if(!strcmp(CmdBuffer, "\\ldots"))
realdl = dtLDots;
if(!strcmp(CmdBuffer, "\\dots"))
realdl = dtLDots;
if(realdl != dtUnknown)
{
dotlev = CheckDots(CmdPtr, BufPtr);
if(dotlev && (dotlev != realdl))
{
TmpPtr = Dot2Str(dotlev);
PSERRA(CmdPtr - Buf, CmdLen, emEllipsis, TmpPtr);
}
}
if(HasWord(CmdBuffer, &WipeArg))
{
if(*BufPtr == '[') /* ] */
ArgEndPtr = GetLTXArg(BufPtr, ArgBuffer, '['); /* [ */
else
ArgEndPtr = BufPtr;
if(ArgEndPtr)
ArgEndPtr = GetLTXArg(ArgEndPtr, ArgBuffer, '{'); /* } */
if(ArgEndPtr)
strwrite(BufPtr, VerbClear, ArgEndPtr - BufPtr);
else
PSERR(CmdPtr - Buf, CmdLen, emNoArgFound);
}
}
/*
* Isolate & check abbreviations.
*/
static void CheckAbbrevs(void)
{
/* Search for abbrevs... */
ULONG Count, CmdLen;
int Char;
STRPTR TmpPtr, AbbPtr;
if(INUSE(emInterWord))
{
BOOL HasAlpha = FALSE;
strcpy(TmpBuffer, Buf);
strupr(TmpBuffer);
for(Count = 0;
Count < SlowAbbrev.Stack.Used;
Count++)
{
for(AbbPtr = TmpBuffer;
(AbbPtr = strstr(AbbPtr, SlowAbbrev.Stack.Data[Count]));
AbbPtr++)
{
CmdLen = strlen(SlowAbbrev.Stack.Data[Count]);
Char = AbbPtr[CmdLen];
if(LATEX_SPACE(Char))
PrintError(CurStkName(&InputStack), RealBuf,
(AbbPtr - TmpBuffer) + CmdLen, 1, Line,
emInterWord);
}
}
/*
* We define an abbrev to be any word (e.g. alphas separated with
* non-alpha characters) containing punctums. For instance:
* e.g. foo.bar.
*
* This will _not_ catch abbrevs like etc., so we'll
* keep the .chktexrc ABBREV keyword.
*/
Count = 0; /* Amount of . in a word - 1 */
for(AbbPtr = TmpBuffer;
(Char = *AbbPtr++);
)
{
if(Char == '.')
{
if(HasAlpha && LATEX_SPACE(*AbbPtr) && Count)
PSERR(AbbPtr - TmpBuffer, 1, emInterWord);
Count++;
}
else if(!isalpha(Char))
{
Count = 0;
HasAlpha = FALSE;
}
else
HasAlpha = TRUE;
}
/* First isolate abbrev, then hold it against table. */
/* NOTE - this modifies TmpBuffer! */
TmpPtr = TmpBuffer;
for(AbbPtr = Buf;
(Char = *AbbPtr);
AbbPtr++)
{
if(isalpha(Char) || (Char == '.'))
*TmpPtr++ = Char;
else
{
*TmpPtr-- = 0;
if(LATEX_SPACE(Char) && (*TmpPtr == '.'))
{
strupr(TmpBuffer);
if(HasWord(TmpBuffer, &Abbrev))
PSERR(AbbPtr- Buf, 1, emInterWord);
}
TmpPtr = TmpBuffer;
}
}
}
}
/*
* Check misc. things which can't be included in the main loop.
*
*/
static void CheckRest(void)
{
ULONG Count;
LONG CmdLen;
STRPTR UsrPtr;
/* Search for user-specified warnings */
if(INUSE(emUserWarn))
{
strcpy(TmpBuffer, Buf);
for(Count = 0L;
Count < UserWarn.Stack.Used;
Count++)
{
for(UsrPtr = TmpBuffer;
(UsrPtr = strstr(UsrPtr, UserWarn.Stack.Data[Count]));
UsrPtr++)
{
CmdLen = strlen(UserWarn.Stack.Data[Count]);
PSERR(UsrPtr - TmpBuffer, CmdLen, emUserWarn);
}
}
}
}
/*
* Checks that the dash-len is correct.
*/
static void CheckDash(void)
{
STRPTR TmpPtr;
int TmpC;
LONG TmpCount, Len;
struct WordList *wl = NULL;
ULONG i;
BOOL Errored = FALSE;
STRPTR PrePtr = &BufPtr[-2];
TmpPtr = BufPtr;
SKIP_AHEAD(TmpPtr, TmpC, TmpC == '-');
TmpCount = TmpPtr - BufPtr + 1;
if(MathMode)
{
if(TmpCount > 1)
HERE(TmpCount, emWrongDash);
}
else
{
if(LATEX_SPACE(*PrePtr) && LATEX_SPACE(*TmpPtr))
wl = &WordDash;
if(isdigit(*PrePtr) && isdigit(*TmpPtr))
wl = &NumDash;
if(isalpha(*PrePtr) && isalpha(*TmpPtr))
wl = &HyphDash;
if(wl)
{
Errored = TRUE;
for(i = 0; i < wl->Stack.Used; i++)
{
Len = strtol(wl->Stack.Data[i], NULL, 0);
if(TmpCount == Len)
{
Errored = FALSE;
break;
}
}
if(Errored)
HERE(TmpCount, emWrongDash);
}
}
}
/*
* Searches the `Buf' for possible errors, and prints the errors. `Line'
* is supplied for error printing.
*/
BOOL FindErr(const STRPTR _RealBuf, const ULONG _Line)
{
STRPTR
CmdPtr, /* We'll have to copy each command out. */
PrePtr, /* Ptr to char in front of command, NULL if
* the cmd appears as the first character */
TmpPtr, /* Temporary pointer */
ErrPtr; /* Ptr to where an error started */
UBYTE
TmpC, /* Just a temp var used throughout the proc.*/
MatchC = 0,
Char; /* Char. currently processed */
ULONG
BrOffset, /* Offset into BrOrder array */
CmdLen; /* Length of misc. things */
BOOL MixingQuotes;
int (*pstcb)(int c);
struct ErrInfo
*ei;
/*
static const Characters we'll do some magic with
UBYTE MgcChars [] = "$\\{}[]()-^_\"´`\'.xX";
*/
UBYTE
ABuf [2] = {0},
BBuf [2] = {0};
enum DotLevel dotlev;
LineCpy = NULL;
if(_RealBuf)
{
RealBuf = _RealBuf;
Line = _Line;
BufPtr = PreProcess();
BufPtr = SkipVerb();
while(BufPtr && *BufPtr)
{
PrePtr = BufPtr - 1;
switch(Char = *BufPtr++)
{
case 'X':
case 'x':
TmpPtr = PrePtr;
SKIP_BACK(TmpPtr, TmpC,
(LATEX_SPACE(TmpC) || strchr("{}$", TmpC)));
if(isdigit(*TmpPtr))
{
TmpPtr = BufPtr;
SKIP_AHEAD(TmpPtr, TmpC,
(LATEX_SPACE(TmpC) || strchr("{}$", TmpC)));
if(isdigit(*TmpPtr))
HERE(1, emUseTimes);
}
break;
case '.':
if((Char == *BufPtr) && (Char == BufPtr[1]))
{
dotlev = CheckDots(&PrePtr[1], &BufPtr[2]);
TmpPtr = Dot2Str(dotlev);
HEREA(3, emEllipsis, TmpPtr);
}
if(!MathMode && islower(*PrePtr))
{
TmpPtr = BufPtr;
SKIP_AHEAD(TmpPtr, TmpC, LATEX_SPACE(TmpC));
if((TmpPtr != BufPtr) && islower(*TmpPtr))
PSERR(BufPtr - Buf, TmpPtr - BufPtr, emInterWord);
}
break;
case '\'':
case '`':
if((Char == *BufPtr) && (Char == BufPtr[1]))
{
PrintError(CurStkName(&InputStack), RealBuf,
BufPtr - Buf - 1, 3, Line,
emThreeQuotes,
Char, Char, Char,
Char, Char, Char);
}
if(Char == '\'')
MatchC = '`';
else
MatchC = '\'';
TmpPtr = BufPtr;
SKIP_AHEAD(TmpPtr, TmpC, TmpC == Char);
MixingQuotes = FALSE;
if((*TmpPtr == MatchC) || (*TmpPtr == '\"') ||
(*TmpPtr == '´'))
MixingQuotes = TRUE;
SKIP_AHEAD(TmpPtr, TmpC, strchr("`\'\"´", TmpC));
if(MixingQuotes)
HERE(TmpPtr - BufPtr + 1, emQuoteMix);
switch(Char)
{
case '\'':
if(isalpha(*TmpPtr) &&
(strchr(LATEX_PUNCT, *PrePtr) || isspace(*PrePtr)))
HERE(TmpPtr - BufPtr + 1, emBeginQ);
break;
case '`':
if(isalpha(*PrePtr) &&
(strchr(LATEX_PUNCT, *TmpPtr) || isspace(*TmpPtr)))
HERE(TmpPtr - BufPtr + 1, emEndQ);
break;
}
BufPtr = TmpPtr;
break;
case '"':
HERE(1, emUseQuoteLiga);
break;
case 180: /* ´ */
HERE(1, emUseOtherQuote);
break;
case '_':
case '^':
if(*PrePtr != '\\')
{
TmpPtr = PrePtr;
SKIP_BACK(TmpPtr, TmpC, LATEX_SPACE(TmpC));
CmdLen = 1;
switch(TmpC)
{
/*{*/
case '}':
if(PrePtr[-1] != '\\')
break;
CmdLen++;
PrePtr--;
/* FALLTHRU */
/*[(*/
case ')':
case ']':
case 0:
PSERR(PrePtr - Buf, CmdLen, emEnclosePar);
}
if((TmpPtr = strip(BufPtr, STRP_LFT)))
{
ErrPtr = TmpPtr;
if(isalpha(*TmpPtr))
pstcb = &my_isalpha;
elif(isdigit(*TmpPtr))
pstcb = &my_isdigit;
else
break;
while((*pstcb)(*TmpPtr++))
;
TmpPtr--;
if((TmpPtr - ErrPtr) > 1)
PSERR(ErrPtr - Buf, TmpPtr - ErrPtr, emEmbrace);
}
}
break;
case '-':
CheckDash();
break;
case '\\': /* Command encountered */
CmdPtr = CmdBuffer;
*CmdPtr++ = Char;
*CmdPtr++ = Char = *BufPtr++;
if(istex(Char))
{
while(istex(Char))
Char = *CmdPtr++ = *BufPtr++;
BufPtr--;
CmdPtr--;
}
*CmdPtr = 0;
/* We've now isolated the command */
if(LATEX_SPACE(*PrePtr))
{
if(HasWord(CmdBuffer, &Linker))
PSERR(PrePtr - Buf, 1, emNBSpace);
if(HasWord(CmdBuffer, &PostLink))
PSERR(PrePtr - Buf, 1, emFalsePage);
}
if(LATEX_SPACE(Char) && !MathMode &&
(!HasWord(CmdBuffer, &Silent)) &&
(strlen(CmdBuffer) != 2))
{
PSERR(BufPtr - Buf, 1, emSpaceTerm);
}
elif((*BufPtr == '\\') && (!isalpha(BufPtr[1])) &&
(!LATEX_SPACE(BufPtr[1])))
{
PSERR(BufPtr - Buf, 2, emNotIntended);
}
PerformBigCmd(PrePtr + 1);
BufPtr = SkipVerb();
break;
/*{{*/
case '}':
/* This should be implemented via the stacking below FIXME */
TmpPtr = BufPtr;
while(*TmpPtr++ == '}')
;
TmpPtr--;
if((*PrePtr != '\\') && !strchr(SMALL_PUNCT, *TmpPtr))
{
if(ItState == itOn)
PSERR(PrePtr - Buf + 1, 1,emNoItFound);
}
ItState = itOff;
/* FALLTHRU */
case '{': /* } */
case '(':
case '[':
case ')':
case ']':
AddBracket(Char);
if((BrOffset = BrackIndex(Char)) != ~0UL)
{
if(BrOffset & 1) /* Closing bracket of some sort */
{
if((ei = PopErr(&CharStack)))
{
TmpC = MatchBracket(*(ei->Data));
FreeErrInfo(ei);
}
else
TmpC = 0;
if(TmpC != Char)
{
ABuf[0] = TmpC;
BBuf[0] = Char;
ABuf[1] = BBuf[1] = 0;
if(TmpC)
PrintError(CurStkName(&InputStack), RealBuf,
BufPtr - Buf - 1, 1, Line,
emExpectC,
ABuf, BBuf);
else
HEREA(1, emSoloC, BBuf);
}
}
else /* Opening bracket of some sort */
{
if(!LineCpy)
LineCpy = strdup(RealBuf);
if(LineCpy)
{
ifn(PushChar(Char, Line, PrePtr - Buf + 1,
&CharStack, LineCpy))
PrintPrgErr(pmNoStackMem);
}
else
PrintPrgErr(pmStrDupErr);
}
}
break;
case '$':
if(*PrePtr != '\\')
{
if(*BufPtr == '$')
BufPtr++;
MathMode ^= TRUE;
}
break;
default:
if(isalpha(Char) && !isalpha(*PrePtr) &&
(*PrePtr != '\\') && MathMode)
{
TmpPtr = BufPtr;
CmdPtr = CmdBuffer;
do
{
*CmdPtr++ = Char;
Char = *TmpPtr++;
} while(isalpha(Char));
*CmdPtr = 0;
if(HasWord(CmdBuffer, &MathRoman))
HEREA(strlen(CmdBuffer), emWordCommand, CmdBuffer);
}
/*
* Search for multiple spaces in input; space in front
* of punctuation.
*/
if(LATEX_SPACE(Char))
{
TmpPtr = BufPtr;
SKIP_AHEAD(TmpPtr, TmpC, LATEX_SPACE(TmpC));
if(*TmpPtr)
{
if((TmpPtr - BufPtr) > 0)
{
HERE(TmpPtr - BufPtr + 1, emMultiSpace);
strwrite(BufPtr, VerbClear, TmpPtr - BufPtr - 1);
}
}
if(*BufPtr && strchr(LATEX_PUNCT, *BufPtr))
HERE(2, emSpacePunct);
}
if(isupper(*PrePtr) && strchr(LATEX_PUNCT, Char) &&
LATEX_SPACE(*BufPtr) && !LATEX_SPACE(PrePtr[-1]))
HERE(1, emInterSent);
}
}
if(!VerbMode)
{
CheckAbbrevs();
CheckRest();
}
}
return(TRUE);
}
/*
* Prints the status/conclusion after doing all the testing, including
* bracket stack status, math mode, etc.
*/
void PrintStatus(ULONG Lines)
{
ULONG Cnt;
struct ErrInfo *ei;
while((ei = PopErr(&CharStack)))
{
PrintError(ei->File, ei->LineBuf, ei->Column,
ei->ErrLen, ei->Line, emNoMatchC,
(STRPTR) ei->Data);
FreeErrInfo(ei);
}
while((ei = PopErr(&EnvStack)))
{
PrintError(ei->File, ei->LineBuf, ei->Column,
ei->ErrLen, ei->Line, emNoMatchC,
(STRPTR) ei->Data);
FreeErrInfo(ei);
}
if(MathMode)
{
PrintError(CurStkName(&InputStack), "", 0L, 0L, Lines,
emMathStillOn);
}
for(Cnt = 0L; Cnt < (NUMBRACKETS>>1); Cnt++)
{
if(Brackets[Cnt << 1] != Brackets[(Cnt << 1) + 1])
{
PrintError(CurStkName(&InputStack), "", 0L, 0L, Lines,
emNoMatchCC,
BrOrder[Cnt<<1], BrOrder[(Cnt<<1) + 1]);
}
}
if(!Quiet)
fprintf(stderr,
"%ld error(s) printed; %ld warning(s) printed; %ld user suppressed warnings.\n",
ErrPrint, WarnPrint, UserSupp);
}
/*
* Scans the `Buf' for a LaTeX arg, and puts that arg into `Dest'. `Delim'
* is the leading character for the arg (either `{' or `[', that is).
* Returns NULL if we can't find the argument, ptr to the first character
* after the argument in other cases.
*
* Dest == NULL => we won't copy anything out.
*
*/
STRPTR GetLTXArg(STRPTR Buf, STRPTR Dest, const UBYTE Delim)
{
UBYTE mileD, TmpC;
STRPTR Retval = NULL;
ULONG DeliCnt = 1,
BufCnt = 0L;
if(Dest)
*Dest = 0;
if((mileD = MatchBracket(Delim)))
{
Buf = strip(Buf, STRP_LFT);
if((TmpC = *Buf) == Delim)
{
while(DeliCnt > 0L)
{
TmpC = *++Buf;
if(Dest)
{
if(BufCnt++ < (BUFSIZ - 1))
*Dest++ = TmpC;
else
break;
}
if(TmpC == Delim)
DeliCnt++;
elif(TmpC == mileD)
DeliCnt--;
elif(!TmpC)
break;
}
if(Dest)
*--Dest = 0L;
Retval = ++Buf;
}
else if(TmpC)
{
if(Dest)
{
Dest[0] = TmpC;
Dest[1] = 0L;
}
Retval = ++Buf;
}
}
return(Retval);
}
#define PRINT_MESSAGE va_start(MsgArgs, Error); \
vfprintf(OutputFile, LaTeXMsgs[Error].Message, MsgArgs); \
va_end(MsgArgs)
/*
* Uses OutputFormat. Be sure that `String'
* does not contain tabs, newlines, etc.
* Prints a formatted string. Formatting codes understood:
* %b - string to print Between fields (from -s option)
* %c - Column position of error
* %d - lenght of error (Digit)
* %f - current Filename
* %i - Turn on inverse printing mode.
* %I - Turn off inverse printing mode.
* %k - Kind of error (warning, error, message)
* %l - Line number of error
* %m - warning Message
* %n - warning Number
* %u - an Underlining line (like the one which appears when using -v1)
* %r - part of line in front of error ('S' - 1)
* %s - part of line which contains error (String) * %t - part of line after error ('S' + 1)
*/
void PrintError(const STRPTR File, const STRPTR String,
const LONG Position, const LONG Len,
const LONG Line, const enum ErrNum Error, ...)
{
static /* Just to reduce stack usage... */
UBYTE PrintBuffer[BUFSIZ];
va_list MsgArgs;
STRPTR LastNorm = OutputFormat,
of = OutputFormat;
int c;
enum Context Context;
if(betw(emMinFault, Error, emMaxFault) && LaTeXMsgs[Error].InUse)
{
do
{
Context = LaTeXMsgs[Error].Context;
if(!HeadErrOut)
Context |= ctOutHead;
#define RGTCTXT(Ctxt, Var) if((Context & Ctxt) && !(Var)) break;
RGTCTXT(ctInMath, MathMode);
RGTCTXT(ctOutMath, !MathMode);
RGTCTXT(ctInHead, InHeader);
RGTCTXT(ctOutHead, !InHeader);
switch(LaTeXMsgs[Error].Type)
{
case etWarn:
WarnPrint++;
break;
case etErr:
ErrPrint++;
break;
case etMsg:
break;
}
while((of = strchr(LastNorm, '%')))
{
c = *of;
*of = 0;
fputs(LastNorm, OutputFile);
*of++ = c;
switch(c = *of++)
{
case 'b':
fputs(Delimit, OutputFile);
break;
case 'c':
fprintf(OutputFile, "%ld", Position + 1);
break;
case 'd':
fprintf(OutputFile, "%ld", Len);
break;
case 'f':
fputs(File, OutputFile);
break;
case 'i':
fputs(PRE_ERROR_STR, OutputFile);
break;
case 'I':
fputs(POST_ERROR_STR, OutputFile);
break;
case 'k':
switch(LaTeXMsgs[Error].Type)
{
case etWarn:
fprintf(OutputFile, "Warning");
break;
case etErr:
fprintf(OutputFile, "Error");
break;
case etMsg:
fprintf(OutputFile, "Message");
break;
}
break;
case 'l':
fprintf(OutputFile, "%ld", Line);
break;
case 'm':
PRINT_MESSAGE;
break;
case 'n':
fprintf(OutputFile, "%d", Error);
break;
case 'u':
sfmemset(PrintBuffer, ' ', (LONG) Position);
sfmemset(&PrintBuffer[Position], '^', Len);
PrintBuffer[Position + Len] = 0;
fputs(PrintBuffer, OutputFile);
break;
case 'r':
strmid(String, PrintBuffer, 0L, Position);
fputs(PrintBuffer, OutputFile);
break;
case 's':
strmid(String, PrintBuffer, Position, Len);
fputs(PrintBuffer, OutputFile);
break;
case 't':
strmid(String, PrintBuffer, Position + Len, LONG_MAX);
fputs(PrintBuffer, OutputFile);
break;
default:
fputc(c, OutputFile);
break;
}
LastNorm = of;
}
fputs(LastNorm, OutputFile);
} while(FALSE);
}
else
UserSupp++;
}
#undef PRINT_MESSAGE
/*
* All commands isolated is routed through this command, so we can
* update global statuses like math mode and whether @ is a letter
* or not.
*/
enum ErrNum PerformCommand(const STRPTR Cmd, STRPTR Arg)
{
STRPTR Argument = "";
enum ErrNum
en = emMinFault;
if(HasWord(Cmd, &Italic))
ItState = itOn;
elif(!strcmp(Cmd, "\\makeatletter"))
AtLetter = TRUE;
elif(!strcmp(Cmd, "\\makeatother"))
AtLetter = FALSE;
elif(InputFiles && !(strcmp(Cmd, "\\input") && strcmp(Cmd, "\\include")))
{
Arg = strip(Arg, STRP_LFT);
if(*Arg == '{') /* } */
{
if(GetLTXArg(Arg, TmpBuffer, '{')) /* } */
Argument = TmpBuffer;
}
else
Argument = strip(Arg, STRP_BTH);
ifn(Argument && PushFileName(Argument, &InputStack))
en = emNoCmdExec;
}
elif(*Cmd == '\\')
{
/* Quicker check of single lettered commands. */
switch(Cmd[1])
{
case '(':
case '[':
MathMode = TRUE;
break;
case ']':
case ')':
MathMode = FALSE;
break;
case '/':
switch(ItState)
{
case itOn:
ItState = itCorrected;
Argument = Arg;
while(*Argument++ == '}')
;
Argument--;
if(strchr(".,", *Argument))
en = emItPunct;
break;
case itCorrected:
en = emItDup;
break;
case itOff:
en = emItInNoIt;
}
break;
}
}
return(en);
}